package com.econ.powercloud.ems.service.biz;

import com.alibaba.fastjson.JSON;
import com.econ.powercloud.common.base.facade.enums.StatusType;
import com.econ.powercloud.common.base.facade.exception.CommonRuntimeException;
import com.econ.powercloud.common.base.facade.util.JacksonUtil;
import com.econ.powercloud.ems.common.enums.DitTypeEnum;
import com.econ.powercloud.ems.common.enums.FaultDesc;
import com.econ.powercloud.ems.common.enums.RemoteType;
import com.econ.powercloud.ems.common.enums.YkYtLocationType;
import com.econ.powercloud.ems.constant.SysConstant;
import com.econ.powercloud.ems.dal.entity.*;
import com.econ.powercloud.ems.dal.entity.ext.IotViceDeviceYkYtExt;
import com.econ.powercloud.ems.pojo.*;
import com.econ.powercloud.ems.pojo.DeviceYtYkShowExtVO;
import com.econ.powercloud.ems.pojo.scene.FeedbackRealDataVO;
import com.econ.powercloud.ems.pojo.scene.IssueValSplitVO;
import com.econ.powercloud.ems.service.common.QueryFlashDataCommonService;
import com.econ.powercloud.ems.service.dao.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import javassist.expr.NewArray;
import lombok.val;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author : zjf
 * @Description : 遥控遥调展示service
 * @Date : 2022/1/10 9:11
 * @Modified by : zjf
 */
@Service
public class EmsYkYtShowService {

    @Autowired
    private EmsDeviceDAO emsDeviceDAO;
    @Autowired
    private EmsYkYtRecordDAO emsYkYtRecordDAO;
    @Autowired
    private QueryFlashDataCommonService queryFlashDataCommonService;
    @Autowired
    private ExtIotViceProductYtDAO extIotViceProductYtDAO;
    @Autowired
    private ExtIotViceProductYkDAO extIotViceProductYkDAO;
    @Autowired
    private FlashNewDataService flashNewDataService;
    @Autowired
    private IotProductYkYtDitDAO iotProductYkYtDitDAO;

    /**
     * 获取PCU遥调数据
     * @param deviceCode: PCS设备code
     */
    public List<DeviceYtShowVO> getPCUModuleYt(String deviceCode){
        //查询该PCS设备下的PCU设备
        List<EmsDevice> pcuDeviceList = emsDeviceDAO.selectChildByDeviceCodes(Lists.newArrayList(deviceCode));
        if(CollectionUtils.isEmpty(pcuDeviceList)){
            return Collections.emptyList();
        }
        return this.getDevicesYtByLocation(pcuDeviceList, YkYtLocationType.PCS_PCU_YT.getCode());
    }

    /**
     * 获取PCS的充放电模式
     * @param deviceCode : PCS设备
     */
    public DeviceYkShowVO getPCSChargeYk(String deviceCode){
        EmsDevice pcsDevice = emsDeviceDAO.selectByDeviceCode(deviceCode);
        Assert.notNull(pcsDevice, FaultDesc.FAILURE_NULL.getDesc());
        List<DeviceYkShowVO> deviceYkShowVOS = this.getDevicesYkByLocation(Lists.newArrayList(pcsDevice), YkYtLocationType.PCS_CHARGE_MODEL.getCode());
        return CollectionUtils.isEmpty(deviceYkShowVOS)? DeviceYkShowVO.builder().deviceCode(deviceCode).build():deviceYkShowVOS.get(0);
    }

    /**
     * 获取PCS遥调数据
     * @param deviceCode: PCS设备code
     */
    public DeviceYtShowVO getPCSYt(String deviceCode){
        //查询该PCS设备下的PCU设备
        EmsDevice pcsDevice = emsDeviceDAO.selectByDeviceCode(deviceCode);
        if(pcsDevice == null){
            throw new CommonRuntimeException(StatusType.FAIL, "设备不存在");
        }
        List<DeviceYtShowVO> result = this.getDevicesYtByLocation(Collections.singletonList(pcsDevice), YkYtLocationType.PCS_PCU_YT.getCode());
        return CollectionUtils.isEmpty(result)?DeviceYtShowVO.builder().deviceName(pcsDevice.getDeviceName())
                .deviceCode(pcsDevice.getDeviceCode()).showVOS(Collections.emptyList()).build():result.get(0);
    }

    /**
     * 获取PCS遥控数据
     * @param deviceCode: PCS设备code
     */
    public DeviceYkShowVO getPCSYk(String deviceCode){
        EmsDevice pcsDevice = emsDeviceDAO.selectByDeviceCode(deviceCode);
        Assert.notNull(pcsDevice, FaultDesc.FAILURE_NULL.getDesc());
        List<DeviceYkShowVO> deviceYkShowVOS = this.getDevicesYkByLocation(Collections.singletonList(pcsDevice), YkYtLocationType.HIGH_PCS_YK.getCode());
        //补充yx反馈点号信息
        FillYxRealDitInfo(pcsDevice,deviceYkShowVOS);
        return CollectionUtils.isEmpty(deviceYkShowVOS)? DeviceYkShowVO.builder().deviceCode(deviceCode).showVOS(Collections.emptyList()).build():deviceYkShowVOS.get(0);
    }

    private void FillYxRealDitInfo(EmsDevice device,List<DeviceYkShowVO> deviceYkShowVOS) {
        String productKey = device.getProductKey();
        List<String> ykDits = Lists.newArrayList();
        for (int i = 0 ; i < deviceYkShowVOS.size() ; i++){
            DeviceYkShowVO showVO = deviceYkShowVOS.get(i);
            ykDits.addAll(showVO.getShowVOS().stream().map(YkValShowVO::getRemoteDit).collect(Collectors.toList()));
        }
        //获取配置了实时反馈yx点号的yk点号信息
        List<IotProductYkYtDit> iotProductYkYtDits = iotProductYkYtDitDAO.selectListByProductKeysAndDits(Collections.singletonList(productKey),ykDits).stream().filter(d->d.getActualDit()!=null).collect(Collectors.toList());
        Map<String,String> YkYxRelationMap = iotProductYkYtDits.stream().collect(Collectors.toMap(IotProductYkYtDit::getDit,IotProductYkYtDit::getActualDit));
        List<DitDataVO> ditDataVOS = Lists.newArrayList();
        iotProductYkYtDits.forEach(d->{
            DitDataVO ditDataVO = new DitDataVO();
            ditDataVO.setDit(d.getActualDit());
            ditDataVO.setInitDeviceCode(device.getInitDeviceCode());
            ditDataVO.setDitType(DitTypeEnum.YX.getCode());
            ditDataVOS.add(ditDataVO);
        });
        List<FlashDataVO> flashYxDataVOS = flashNewDataService.getSvgFlashVO(ditDataVOS);
        Map<String,FlashDataVO> yxDataMap = flashYxDataVOS.stream().collect(Collectors.toMap(FlashDataVO::getDit,d->d));
        //把查到的yx实时数据填入对应的yk对象信息中返回
        deviceYkShowVOS.forEach(d->{
            d.getShowVOS().forEach(v->{
                String keyYk = v.getRemoteDit();
                String keyYx = YkYxRelationMap.get(keyYk);
                if (keyYx!=null){
                    FlashDataVO yxData = yxDataMap.get(keyYx);
                    v.setYxRealDitVO(yxData);
                }
            });
        });
    }


    /**
     * 根据设备集合 与 遥调位置信息获取遥调展示数据
     * 即需要展示 下发值，实时值，反馈值
     * @param deviceList
     * @param ytLocation
     * @return
     */
    public List<DeviceYtShowVO> getDevicesYtByLocation(List<EmsDevice> deviceList, Integer ytLocation){
        if(CollectionUtils.isEmpty(deviceList)){
            return Collections.emptyList();
        }
        List<String> deviceCodes = deviceList.stream().map(EmsDevice::getDeviceCode).collect(Collectors.toList());
        Map<String, EmsDevice> deviceMap = deviceList.stream().collect(Collectors.toMap(EmsDevice::getDeviceCode, d->d,(p1, p2)->p1));
        List<String> initDeviceCodes = Lists.newArrayList(deviceList.stream().map(EmsDevice::getInitDeviceCode).collect(Collectors.toSet()));
        //查询场景配置信息中的遥控遥调副表的该位置的配置信息
        List<IotViceDeviceYkYtExt> ytExts = extIotViceProductYtDAO.getBaseMapper().selectYkYtByInitDeviceCodeAndLocation(initDeviceCodes, ytLocation);
        List<IotViceDeviceYkYtExt> ykExts = extIotViceProductYkDAO.getBaseMapper().selectYkYtByInitDeviceCodeAndLocation(initDeviceCodes, ytLocation);
        ykExts.addAll(ytExts);
        Map<String,List<IotViceDeviceYkYtExt>> ytYkShowMap = ykExts.stream().collect(Collectors.groupingBy(item -> item.getInitDeviceCode() + SysConstant.SPOT + item.getModuleCode()));
        //遍历设备,封装每一个设备需要展示的遥调数据，key:deviceCode
        Map<String,List<DeviceYtYkShowExtVO>> ytYkShowExtMap = Maps.newLinkedHashMap();
        //封装需要查询的实时数据对象(即反馈值与实时值)
        List<EmsDeviceLocationVO> locationVOList = Lists.newArrayList();
        deviceList.stream().forEach(d->{
            //先不管是否存在特殊配置,把配置数据生成
            List<DeviceYtYkShowExtVO> extDTOList = Lists.newArrayList();
            List<IotViceDeviceYkYtExt> ykYtExts = ytYkShowMap.get(d.getInitDeviceCode() + SysConstant.SPOT + d.getModuleCode());
            if(CollectionUtils.isNotEmpty(ykYtExts)){
                ykYtExts.stream().forEach(f->{
                    DeviceYtYkShowExtVO showExtDTO = JacksonUtil.convertToObj(f, DeviceYtYkShowExtVO.class);
                    showExtDTO.setDeviceCode(d.getDeviceCode());
                    if(!StringUtils.isEmpty(f.getFeedbackRealData())){
                        FeedbackRealDataVO dataVO = JSON.parseObject(f.getFeedbackRealData(),FeedbackRealDataVO.class);
                        if(dataVO.getFeedbackDit() != null){
                            //封装需要查询的反馈值
                            locationVOList.add(EmsDeviceLocationVO.builder().initDeviceCode(d.getInitDeviceCode())
                                    .pid(d.getInitDeviceCode() + SysConstant.SPOT + dataVO.getFeedbackDit())
                                    .dit(dataVO.getFeedbackDit())
                                    .build());
                            showExtDTO.setFeedbackPid(d.getInitDeviceCode() + SysConstant.SPOT + dataVO.getFeedbackDit());
                        }
                        if(dataVO.getRealDit() != null){
                            //封装需要查询的实时值
                            locationVOList.add(EmsDeviceLocationVO.builder().initDeviceCode(d.getInitDeviceCode())
                                    .pid(d.getInitDeviceCode() + SysConstant.SPOT + dataVO.getRealDit())
                                    .dit(dataVO.getRealDit())
                                    .build());
                            showExtDTO.setRealPid(d.getInitDeviceCode() + SysConstant.SPOT + dataVO.getRealDit());
                        }
                    }
                    extDTOList.add(showExtDTO);
                });
                ytYkShowExtMap.put(d.getDeviceCode(),extDTOList.stream().sorted(Comparator.comparing(DeviceYtYkShowExtVO::getOrderNum)).collect(Collectors.toList()));
            }
        });
        //查询遥调历史数据信息
        List<EmsYkYtRecord> ykYtRecordList = emsYkYtRecordDAO.getListByDeviceCodes(deviceCodes);
        //按照deviceCode + . + dit + . + remoteType转map
        Map<String,Double> ykYtRecordMap = Maps.newHashMap();
        if(CollectionUtils.isNotEmpty(ykYtRecordList)){
            ykYtRecordList.stream().forEach(f->{
                ykYtRecordMap.put(f.getDeviceCode() + SysConstant.SPOT + f.getDit() + SysConstant.SPOT + f.getRemoteType(), f.getValue());
            });
        }
        //调用查询实时数据接口
        Map<String, FlashDataVO> flashMap = this.getFlashMap(locationVOList);
        //封装对象
        List<DeviceYtShowVO> result = Lists.newArrayList();
        Map<String, EmsDeviceLocationVO> locationVOMap = locationVOList.stream().collect(Collectors.toMap(EmsDeviceLocationVO::getPid, d->d,(p1, p2)->p1));
        ytYkShowExtMap.forEach((k,v)->{
            List<YtShowVO> showVOList = Lists.newArrayList();
            //订阅点号
            List<String> subscribePids = Lists.newArrayList();
            v.stream().forEach(d->{
                showVOList.add(YtShowVO.builder()
                        .name(d.getRemark()).remoteType(d.getRemoteType())
                        .remoteDit(d.getDit()).deviceCode(k).unit(d.getUnit())
                        .ditType(Objects.equals(d.getRemoteType(), RemoteType.YK.getCode())? DitTypeEnum.YK.getCode() : DitTypeEnum.YT.getCode())
                        .issuedVal(ykYtRecordMap.get(d.getDeviceCode() + SysConstant.SPOT + d.getDit() + SysConstant.SPOT + d.getRemoteType()))
                        .rangeStart(d.getRangeStart()).rangeEnd(d.getRangeEnd())
                        .flashVal(d.getRealPid() == null? null : this.getFlashVO(locationVOMap.get(d.getRealPid()),flashMap))
                        .feedbackVal(d.getFeedbackPid() == null? null : this.getFlashVO(locationVOMap.get(d.getFeedbackPid()),flashMap))
                        .build());
                if(d.getRealPid() != null){
                    subscribePids.add(d.getRealPid());
                }
                if(d.getFeedbackPid() != null){
                    subscribePids.add(d.getFeedbackPid());
                }
            });
            result.add(DeviceYtShowVO.builder().deviceCode(k).deviceName(deviceMap.get(k).getDeviceName())
                    .showVOS(showVOList).subscribePids(subscribePids).build());
        });
        return result;
    }

    /**
     * 根据设备集合 与 遥控位置信息获取遥控展示数据
     * @param deviceList
     * @param ykLocation
     * @return
     */
    public List<DeviceYkShowVO> getDevicesYkByLocation(List<EmsDevice> deviceList, Integer ykLocation){
        if(CollectionUtils.isEmpty(deviceList)){
            return Collections.emptyList();
        }
        List<String> deviceCodes = deviceList.stream().map(EmsDevice::getDeviceCode).collect(Collectors.toList());
        Map<String, String> deviceCodeNameMap = deviceList.stream().collect(Collectors.toMap(EmsDevice::getDeviceCode, EmsDevice::getDeviceName));
        List<String> initDeviceCodes = Lists.newArrayList(deviceList.stream().map(EmsDevice::getInitDeviceCode).collect(Collectors.toSet()));
        //查询场景配置信息中的遥控遥调副表的该位置的配置信息
        List<IotViceDeviceYkYtExt> ykExts = extIotViceProductYkDAO.getBaseMapper().selectYkYtByInitDeviceCodeAndLocation(initDeviceCodes, ykLocation);
        List<IotViceDeviceYkYtExt> ytExts = extIotViceProductYtDAO.getBaseMapper().selectYkYtByInitDeviceCodeAndLocation(initDeviceCodes, ykLocation);
        ykExts.addAll(ytExts);
        Map<String,List<IotViceDeviceYkYtExt>> ytYkShowMap = ykExts.stream().collect(Collectors.groupingBy(item -> item.getInitDeviceCode() + SysConstant.SPOT + item.getModuleCode()));
        //遍历设备,封装每一个设备需要展示的遥控数据，key:deviceCode
        Map<String,List<DeviceYtYkShowExtVO>> ytYkShowExtMap = Maps.newLinkedHashMap();
        for (EmsDevice d : deviceList){
            //先不管是否存在特殊配置,把配置数据生成
            List<DeviceYtYkShowExtVO> extDTOList = Lists.newArrayList();
            List<IotViceDeviceYkYtExt> emsYtYkShows = ytYkShowMap.get(d.getInitDeviceCode() + SysConstant.SPOT + d.getModuleCode());
            if(CollectionUtils.isEmpty(emsYtYkShows)){
                continue;
            }
            for (IotViceDeviceYkYtExt ykYtExt : emsYtYkShows){
                if(ykYtExt.getValSplit() == null){
                    continue;
                }
                List<IssueValSplitVO> splitVOList = JSON.parseArray(ykYtExt.getValSplit(),IssueValSplitVO.class);
                splitVOList.stream().forEach(f->{
                    DeviceYtYkShowExtVO showExtDTO = JacksonUtil.convertToObj(ykYtExt, DeviceYtYkShowExtVO.class);
                    showExtDTO.setDeviceCode(d.getDeviceCode());
                    showExtDTO.setYkIssueOrderNum(f.getOrderNum());
                    showExtDTO.setVal(f.getIssueVal());
                    showExtDTO.setValName(f.getName());
                    extDTOList.add(showExtDTO);
                });
            }
            //这里需要获取showExtDTOMap中VO的save属性是0的数据
            ytYkShowExtMap.put(d.getDeviceCode(),extDTOList);
        }
        //查询遥调遥控历史数据信息
        List<EmsYkYtRecord> ykYtRecordList = emsYkYtRecordDAO.getListByDeviceCodes(deviceCodes);
        //按照devicode + . + dit + . + remoteType转map
        Map<String,Double> ykYtRecordMap = Maps.newHashMap();
        if(CollectionUtils.isNotEmpty(ykYtRecordList)){
            ykYtRecordList.stream().forEach(f->{
                ykYtRecordMap.put(f.getDeviceCode() + SysConstant.SPOT + f.getDit() + SysConstant.SPOT + f.getRemoteType(), f.getValue());
            });
        }
        //封装对象
        List<DeviceYkShowVO> result = Lists.newArrayList();
        ytYkShowExtMap.forEach((k,v)->{
            List<YkShowVO> showVOList = Lists.newArrayList();
            v.stream().forEach(d->{
                Double hisVal = ykYtRecordMap.get(d.getDeviceCode() + SysConstant.SPOT + d.getDit() + SysConstant.SPOT + d.getRemoteType());
                showVOList.add(YkShowVO.builder()
                        .id(d.getId())
                        .name(d.getValName()).remoteType(d.getRemoteType())
                        .remoteDit(d.getDit()).deviceCode(k)
                        .val(d.getVal())
                        .orderNum(d.getOrderNum())
                        .ykIssueOrderNum(d.getYkIssueOrderNum())
                        .remoteDitName(d.getRemark())
                        //如果遥控的固定值 与 历史设定值相等，就证明这个遥控是上一次的设定值
                        .issuedVal(hisVal ==null?null : d.getVal().intValue() == hisVal.intValue()?hisVal : null)
                        .build());
            });
            Map<String, List<YkShowVO>> map = showVOList.stream().collect(Collectors.groupingBy(YkShowVO::getRemoteDit, LinkedHashMap::new,Collectors.toList()));
            List<YkValShowVO> showVOS = Lists.newArrayList();
            map.forEach((dit, list) -> {
                if (CollectionUtils.isEmpty(list) && list.get(0) == null) {
                    return;
                }
                List<YkValListShowVO> ykValList = list.stream().sorted(Comparator.comparing(YkShowVO::getYkIssueOrderNum)).map(d -> YkValListShowVO.builder()
                        .val(d.getVal()).name(d.getName()).issuedVal(d.getIssuedVal()).build()).collect(Collectors.toList());
                YkShowVO ykShowVO = list.get(0);
                YkValShowVO showVO = YkValShowVO.builder().id(ykShowVO.getId()).deviceCode(ykShowVO.getDeviceCode()).remoteDit(ykShowVO.getRemoteDit())
                        .ditType(Objects.equals(ykShowVO.getRemoteType(), RemoteType.YK.getCode())? DitTypeEnum.YK.getCode() : DitTypeEnum.YT.getCode())
                        .orderNum(ykShowVO.getOrderNum())
                        .remoteDitName(ykShowVO.getRemoteDitName()).remoteType(ykShowVO.getRemoteType()).ykValList(ykValList).build();
                showVOS.add(showVO);
            });
            result.add(DeviceYkShowVO.builder().deviceCode(k).deviceName(deviceCodeNameMap.get(k)).showVOS(showVOS.size()>0?showVOS.stream().sorted(Comparator.comparing(YkValShowVO::getOrderNum)).collect(Collectors.toList())
                    :showVOS).build());
        });
        return result;
    }

    /**
     * 查询实时数据,并按照量测类型,pid进行分组
     */
    private Map<String, FlashDataVO> getFlashMap(List<EmsDeviceLocationVO> deviceLocationList){
        Map<String, FlashDataVO> result = Maps.newHashMap();
        List<FlashDataVO> flashVOS = queryFlashDataCommonService.getFlashData(JacksonUtil.convertToList(deviceLocationList, QueryDataVO.class));
        if(CollectionUtils.isEmpty(flashVOS)){
            return result;
        }
        return flashVOS.stream().collect(Collectors.toMap(FlashDataVO::getPid, p -> p));
    }

    /**
     * 获取实时数据
     * @param locationVO : 需要获取的实时数据的封装类
     * @param flashVOMap : 实时数据查询结果Map
     */
    private FlashVO getFlashVO(EmsDeviceLocationVO locationVO, Map<String, FlashDataVO> flashVOMap){
        if(locationVO == null){
            return FlashVO.builder().build();
        }
        FlashVO flashVO = FlashVO.builder().pid(locationVO.getPid()).initDeviceCode(locationVO.getInitDeviceCode())
                .dit(locationVO.getDit())
                .unit(locationVO.getUnit()).name(locationVO.getName()).build();
        //获取查询的实时数据
        FlashDataVO vo = flashVOMap.get(locationVO.getPid());
        if (vo != null) {
            flashVO.setTime(vo.getTime());
            flashVO.setValue(vo.getVal());
            flashVO.setUnit(vo.getUnit());
        }
        return flashVO;
    }
}
